package com.net.common.encryption;

import com.net.common.exception.NetRuntimeException;
import lombok.SneakyThrows;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author hjh
 */
public class RsaUtils {

    @SneakyThrows
    public static KeyPair getKeyPair() {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * base64 编码
     *
     * @param bytes 编码内容
     * @return
     */
    public static String base64Encoder(byte[] bytes) {
        Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(bytes);
    }

    /**
     * base64 编码
     *
     * @param key
     * @return
     */
    public static byte[] base64Decoder(String key) {
        Base64.Decoder decoder = Base64.getDecoder();
        return decoder.decode(key);
    }

    /**
     * 公钥转base64
     *
     * @return
     */
    public static String publicKeyToBase64(PublicKey publicKey) {
        byte[] bytes = publicKey.getEncoded();
        return base64Encoder(bytes);
    }

    /**
     * 私钥转base64
     *
     * @return
     */
    public static String privateKeyToBase64(PrivateKey privateKey) {
        byte[] bytes = privateKey.getEncoded();
        return base64Encoder(bytes);
    }

    /**
     * base64转公钥
     *
     * @param publicKeyBase64
     * @return
     */
    public static PublicKey publicKey(String publicKeyBase64) throws Exception {
        byte[] keyBytes = base64Decoder(publicKeyBase64);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * base64转私钥
     *
     * @param privateKeyBase64
     * @return
     */
    public static PrivateKey privateKey(String privateKeyBase64) throws Exception {
        byte[] keyBytes = base64Decoder(privateKeyBase64);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 公钥加密
     *
     * @param content
     * @param publicKey
     * @return byte[]
     */
    @SneakyThrows
    public static String publicEncrypt(byte[] content, PublicKey publicKey) {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return base64Encoder(cipher.doFinal(content));
    }

    /**
     * 公钥加密
     *
     * @param content
     * @param publicKey
     * @return base 64
     */
    @SneakyThrows
    public static String publicEncrypt(String content, PublicKey publicKey) {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] doFinal = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return base64Encoder(doFinal);
    }

    @SneakyThrows
    public static byte[] publicDecrypt(byte[] content, PublicKey publicKey) {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(content);
    }

    /**
     * 私钥解密
     *
     * @param base64
     * @param privateKey
     * @return
     */
    public static String privateDecrypt(String base64, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(cipher.doFinal(base64Decoder(base64)), StandardCharsets.UTF_8);
        } catch (Exception e) {
            if (e instanceof BadPaddingException) {
                throw new NetRuntimeException("密钥已过期请重新生成!", e);
            }
            throw new NetRuntimeException("密钥错误!", e);
        }
    }

    @SneakyThrows
    public static String privateEncrypt(byte[] content, PrivateKey privateKey) {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return base64Encoder(cipher.doFinal(content));
    }
}
